package cn.yx.common.security.webflux.filter;

import cn.hutool.core.util.StrUtil;
import cn.yx.common.core.util.StringUtil;
import cn.yx.common.security.pojo.JwtCacheToken;
import cn.yx.common.security.pojo.JwtUserDetails;
import cn.yx.common.security.properties.SecurityProperties;
import cn.yx.common.security.service.JwtTokenService;
import cn.yx.common.security.util.JwtUtil;
import cn.yx.common.security.webflux.constant.message.JwtAuthenticationFilterMessageKey;
import cn.yx.common.webflux.util.WebFluxUtil;
import org.springframework.core.annotation.Order;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import org.springframework.web.server.WebFilter;
import org.springframework.web.server.WebFilterChain;
import reactor.core.publisher.Mono;

import java.util.Objects;

/**
 * <p>Jwt认证过滤器。
 * 在UsernamePasswordAuthenticationFilter用户认证过滤器前先判断是否有jwt token传入，如果有，
 * 则从token中获取用户信息，并且检验此token是否有效，如果有效则表示此用户认证通过
 * </p>
 *
 * @author Wgssmart
 */
@Component
@Order(-30)
public class JwtAuthenticationFilter implements WebFilter {

    private final JwtTokenService jwtTokenService;

    private final SecurityProperties securityProperties;

    public JwtAuthenticationFilter(JwtTokenService jwtTokenService, SecurityProperties securityProperties) {
        this.jwtTokenService = jwtTokenService;
        this.securityProperties = securityProperties;
    }

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, WebFilterChain chain) {
        String token = getJwtToken(exchange.getRequest());
        if (StrUtil.isNotEmpty(token)) {
            JwtUserDetails jwtUserDetails = JwtUtil.parseJwtUserDetails(token);
            if (Objects.isNull(jwtUserDetails)) {
                return WebFluxUtil.write(exchange.getResponse(), JwtAuthenticationFilterMessageKey.TOKEN_ERROR);
            }
            // 由token解析出JwtUserDetails
            JwtCacheToken jwtCacheToken = jwtTokenService.getCachedToken(jwtUserDetails);
            if (Objects.isNull(jwtCacheToken)) {
                return WebFluxUtil.write(exchange.getResponse(), JwtAuthenticationFilterMessageKey.TOKEN_NOT_EXIST);
            }
            String tokenInRedis = jwtCacheToken.getToken();
            if (!StrUtil.equals(token, tokenInRedis)) {
                return WebFluxUtil.write(exchange.getResponse(), JwtAuthenticationFilterMessageKey.TOKEN_ERROR);
            }
            // 如果redis中存在此token
            if (Objects.isNull(SecurityContextHolder.getContext().getAuthentication())) {
                // spring security上下文中不存在认证信息，则存储新的authentication到上下文
                saveAuthentication(jwtUserDetails);
            }
            // 只要jwt token正常使用了，就刷新缓存失效时间，注意jwt token本身的过期时间并未变更
            jwtTokenService.refreshTokenExpiration(jwtUserDetails);
        }
        return chain.filter(exchange);
    }

    private String getJwtToken(ServerHttpRequest request) {
        String token = WebFluxUtil.getHeader(request, securityProperties.getJwtToken().getHeader());
        if (StrUtil.isEmpty(token) || !token.startsWith(securityProperties.getJwtToken().getHeaderPrefix())) {
            return null;
        }
        return token.replace(securityProperties.getJwtToken().getHeaderPrefix(), StringUtil.EMPTY_STRING);
    }

    private void saveAuthentication(JwtUserDetails jwtUserDetails) {
        UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(jwtUserDetails,
                null, jwtUserDetails.getAuthorities());
        // 将认证状态存入SpringSecurity上下文
        SecurityContextHolder.getContext().setAuthentication(authentication);
    }

}